package gt

import (
	"reflect"
	"runtime"

	"gitee.com/dark.H/gs"
	"github.com/mattn/go-runewidth"
	"github.com/nsf/termbox-go"
)

var (
	keymap = map[uint16]string{

		0x00: "KeyCtrlSpace",
		0x01: "KeyCtrlA",
		0x02: "KeyCtrlB",
		0x03: "KeyCtrlC",
		0x04: "KeyCtrlD",
		0x05: "KeyCtrlE",
		0x06: "KeyCtrlF",
		0x07: "KeyCtrlG",
		0x08: "KeyCtrlH",
		0x09: "KeyTab|KeyCtrlI",

		0x0A: "KeyCtrlJ",
		0x0B: "KeyCtrlK",
		0x0C: "KeyCtrlL",
		0x0D: "KeyEnter|KeyCtrlM",

		0x0E: "KeyCtrlN",
		0x0F: "KeyCtrlO",
		0x10: "KeyCtrlP",
		0x11: "KeyCtrlQ",
		0x12: "KeyCtrlR",
		0x13: "KeyCtrlS",
		0x14: "KeyCtrlT",
		0x15: "KeyCtrlU",
		0x16: "KeyCtrlV",
		0x17: "KeyCtrlW",
		0x18: "KeyCtrlX",
		0x19: "KeyCtrlY",
		0x1A: "KeyCtrlZ",

		0x1B: "KeyEsc|KeyCtrlLsqBracket|KeyCtrl3",

		0x1C: "KeyCtrlBackslash|KeyCtrl4",

		0x1D: "KeyCtrlRsqBracket|KeyCtrl5",
		0x1E: "KeyCtrl6",

		0x1F: "KeyCtrlUnderscore|KeyCtrlSlash|KeyCtrl7",

		0x20: "KeySpace",
		0x7F: "KeyBackspace2|KeyCtrl8",
	}
)

type PanelHandler struct {
	*Panel
	UseMouse           bool
	buf                gs.Str
	KeyMap2            map[rune]func(p *PanelHandler) bool
	KeyMap             map[termbox.Key]func(p *PanelHandler) bool
	KeyMouseCall       func(mouseEvent termbox.Event, p *PanelHandler) bool
	KeyMouseClick      func(x, y int, p *PanelHandler) bool
	KeyMouseScrollDown func(x, y int, p *PanelHandler) bool
	KeyMouseScrollUp   func(x, y int, p *PanelHandler) bool
	update             func()
	selectedNum        int
	KeyHandle          func(k termbox.Event) bool
	onChanged          func(p *PanelHandler) bool
}

func NewPanelHandler() *PanelHandler {
	p := NewPanel()
	p2 := &PanelHandler{
		Panel:       p,
		selectedNum: -1,
		KeyMap2:     make(map[rune]func(p *PanelHandler) bool),
		KeyMap:      make(map[termbox.Key]func(p *PanelHandler) bool),
		KeyHandle: func(k termbox.Event) bool {
			return false
		},
	}
	p2.defaultKeyMap()
	return p2
}

func (ph *PanelHandler) Copy() *PanelHandler {
	oldp := ph.Panel.Copy()
	nph := &PanelHandler{
		Panel:              oldp,
		UseMouse:           ph.UseMouse,
		buf:                ph.buf,
		KeyMap2:            make(map[rune]func(p *PanelHandler) bool),
		KeyMap:             make(map[termbox.Key]func(p *PanelHandler) bool),
		KeyMouseCall:       ph.KeyMouseCall,
		KeyMouseClick:      ph.KeyMouseClick,
		KeyMouseScrollDown: ph.KeyMouseScrollDown,
		KeyMouseScrollUp:   ph.KeyMouseScrollUp,
		update:             ph.update,
		selectedNum:        ph.selectedNum,
		KeyHandle:          ph.KeyHandle,
		onChanged:          ph.onChanged,
	}
	for k, v := range ph.KeyMap2 {
		nph.KeyMap2[k] = v
	}
	for k, v := range ph.KeyMap {
		nph.KeyMap[k] = v
	}
	return nph
}

func (ph *PanelHandler) defaultKeyMap() {
	ph.Onkey(termbox.KeyEsc, func(parentPanel *PanelHandler) bool {
		return true
	}).Onkey(termbox.KeyCtrlH, func(parentPanel *PanelHandler) bool {
		ph.ShowHelp()
		return false
	})
	ph.OnKeySameAs(termbox.KeyArrowRight, termbox.KeyEnter)
	ph.OnKeySameAs(termbox.KeyArrowLeft, termbox.KeyEsc)
}

func (ph *PanelHandler) OnKeySameAs(keyNew, keyOld termbox.Key) *PanelHandler {
	ph.Onkey(keyNew, func(parentPanel *PanelHandler) bool {
		if call, ok := parentPanel.KeyMap[keyOld]; ok {
			return call(parentPanel)
		}
		return false
	})
	return ph
}

func (ph *PanelHandler) OnKeyChSameAs(key rune, keyOld termbox.Key) *PanelHandler {
	ph.OnkeyCh(key, func(parentPanel *PanelHandler) bool {
		if call, ok := parentPanel.KeyMap[keyOld]; ok {
			return call(parentPanel)
		}
		return false
	})
	return ph
}

func (ph *PanelHandler) OnKeySameAsCh(key termbox.Key, keyOld rune) *PanelHandler {
	ph.Onkey(key, func(parentPanel *PanelHandler) bool {
		if call, ok := parentPanel.KeyMap2[keyOld]; ok {
			return call(parentPanel)
		}
		return false
	})
	return ph
}

func (ph *PanelHandler) LocSize() (x, y, h, w int) {
	x, y = ph.Loc()
	w, h = ph.OuterSize()
	// hh, ww := gs.GetWindowsSize()
	// gs.Str("x%d y%d  w%d h %d . ww%d wh %d").F(x, y, w, h, ww, hh).ToFile("/tmp/loc.txt")
	return
}

func (ph *PanelHandler) Switch(do func(parentPanel *PanelHandler) bool) *PanelHandler {
	defer func() {
		if !termbox.IsInit {
			termbox.Init()
		}

		ph.Draw()
	}()
	if do(ph) {
		termbox.Init()
	}
	return ph
}

// func (ph *PanelHandler) Once(do func(parentPanel *PanelHandler) bool) *PanelHandler {
// 	defer func() {
// 		if !termbox.IsInit {
// 			termbox.Init()
// 		}
// 		ph.Draw()
// 	}()
// 	if do(ph) {
// 		termbox.Init()
// 	}
// 	return ph

// }

func (ph *PanelHandler) GetContentRow(wrap_width ...bool) int {
	if wrap_width != nil && wrap_width[0] {
		return gs.Str(runewidth.Wrap(ph.buf.Str(), ph.Width)).Count("\n")
	}
	return ph.buf.Count("\n")
}

func (t *PanelHandler) ScrollDown() *PanelHandler {
	_, y := t.Pos()
	n := t.GetContentRow() + 1
	win := t.Height - 2 - 3
	// gs.Str("C-h:%d / win:%d\n").F(n, win).ToFile("/tmp/aaa.txt")
	if n > win {
		t.SetPosY(y + 1)
	} else {
		t.SetPosY(0)
	}
	// return t

	return t
}

func (t *PanelHandler) ScrollToTopMax() *PanelHandler {
	t.SetPosY(0)
	return t
}

func (t *PanelHandler) ScrollToTop() *PanelHandler {
	_, y := t.Pos()
	y -= 1
	if y < 0 {
		y = 0
	}
	t.SetPosY(y)
	return t
}

func (t *PanelHandler) ScrollToBottom() *PanelHandler {
	n := t.GetContentRow() + 1
	win := t.Height - 2 - 3
	// gs.Str("C-h:%d / win:%d\n").F(n, win).ToFile("/tmp/aaa.txt")
	if n > win {
		t.SetPosY(n - win)
	} else {
		t.SetPosY(0)
	}
	return t
}

func (ph *PanelHandler) SetDefaultEventHandler(k func(k termbox.Event) bool) *PanelHandler {
	if k != nil {
		ph.KeyHandle = k
	}
	return ph
}

func (ph *PanelHandler) SetMouse(c bool) *PanelHandler {
	if c {
		ph.UseMouse = true
	} else {
		ph.UseMouse = false
	}
	if termbox.IsInit {
		if ph.UseMouse {
			termbox.SetInputMode(termbox.InputMouse)
		} else {
			termbox.SetInputMode(termbox.InputEsc)
		}
	}
	return ph
}

func (ph *PanelHandler) IsInMe(x, y int) bool {
	xl, yl := ph.Loc()
	w, r := ph.InnerSize()
	if x >= xl && x <= xl+w && y >= yl && y <= yl+r {
		return true
	}
	return false
}

func (ph *PanelHandler) Focus(no_clear ...bool) *PanelHandler {
	if !termbox.IsInit {
		err := termbox.Init()
		if err != nil {
			panic(err)
		}
		defer termbox.Close()

	}

	ph.Draw()
	if no_clear == nil {
		defer ph.Clear()
	}
	changed := false
LOOP:
	for {
		if ph.KeyMouseCall != nil || ph.KeyMouseClick != nil || ph.KeyMouseScrollDown != nil || ph.KeyMouseScrollUp != nil {
			if ph.UseMouse {
				ph.SetMouse(true)
			}
		}
		switch ev := termbox.PollEvent(); ev.Type {

		case termbox.EventMouse:
			changed = true
			// ph.LogInfo("/tmp/clic.txt")
			if ph.KeyMouseCall != nil {

				ph.KeyMouseCall(ev, ph)
			} else {
				switch ev.Key {
				case termbox.MouseWheelDown:
					if ph.KeyMouseScrollDown != nil {
						if ph.KeyMouseScrollDown(ev.MouseX, ev.MouseY, ph) {
							break LOOP
						}
					}
				case termbox.MouseWheelUp:
					if ph.KeyMouseScrollUp != nil {
						if ph.KeyMouseScrollUp(ev.MouseX, ev.MouseY, ph) {
							break LOOP
						}
					}
				default:
					if ph.KeyMouseClick != nil {
						if ph.KeyMouseClick(ev.MouseX, ev.MouseY, ph) {
							break LOOP
						}
					}
				}
			}
		case termbox.EventKey:
			changed = true
			switch ev.Key {

			default:

				if call, ok := ph.KeyMap2[ev.Ch]; ok {
					if call(ph) {
						break LOOP
					}
				} else {

					if call, ok := ph.KeyMap[ev.Key]; ok {
						if call(ph) {
							break LOOP
						}
					}
				}
				if ph.KeyHandle(ev) {
					break LOOP
				}
			}
		}
		ph.Draw()
		if changed {
			if ph.onChanged != nil {
				if ph.onChanged(ph) {
					break LOOP
				}
			}
			changed = false
		}
	}
	return ph
}

func (ph *PanelHandler) OnChanged(call func(p *PanelHandler) bool) {
	if call != nil {
		ph.onChanged = call
	}

}

func (ph *PanelHandler) Onkey(key termbox.Key, call func(parentPanel *PanelHandler) bool) *PanelHandler {
	ph.KeyMap[key] = call
	return ph
}

func (ph *PanelHandler) OnkeyCh(key rune, call func(parentPanel *PanelHandler) bool) *PanelHandler {
	ph.KeyMap2[key] = call
	return ph
}

func (ph *PanelHandler) OnkeyEnter(call func(parentPanel *PanelHandler) bool) *PanelHandler {
	ph.KeyMap[termbox.KeyEnter] = call
	return ph
}

func (ph *PanelHandler) OnkeySpace(call func(parentPanel *PanelHandler) bool) *PanelHandler {
	ph.KeyMap[termbox.KeySpace] = call
	return ph
}

func (ph *PanelHandler) OnClickRaw(call func(e termbox.Event, panel *PanelHandler) bool) *PanelHandler {
	if call != nil {
		ph.KeyMouseCall = call
	}
	return ph
}

func (ph *PanelHandler) OnClick(call func(x, y int, panel *PanelHandler) bool) *PanelHandler {
	if call != nil {
		ph.KeyMouseClick = call
	}
	return ph
}

func (ph *PanelHandler) OnClickScrollDown(call func(x, y int, panel *PanelHandler) bool) *PanelHandler {
	if call != nil {
		ph.KeyMouseScrollDown = call
	}
	return ph
}

func (ph *PanelHandler) OnClickScrollUp(call func(x, y int, panel *PanelHandler) bool) *PanelHandler {
	if call != nil {
		ph.KeyMouseScrollUp = call
	}
	return ph
}

func (ph *PanelHandler) OnkeyDel(call func(parentPanel *PanelHandler) bool) *PanelHandler {
	ph.KeyMap[K_DEL] = call
	// ph.KeyMap2[K_DEL] = call
	return ph
}

func (ph *PanelHandler) OnkeyCtrlC(call func(parentPanel *PanelHandler) bool) *PanelHandler {
	ph.KeyMap[termbox.KeyCtrlC] = call
	// ph.KeyMap2[K_DEL] = call
	return ph
}

func (ph *PanelHandler) Label(msg gs.Str) *PanelHandler {
	ph.loc.Tip(msg)
	return ph
}

func (ph *PanelHandler) Write(buf gs.Str) *PanelHandler {
	ph.buf = buf
	return ph
}

func (ph *PanelHandler) Read() (buf gs.Str) {
	return ph.buf
}

func (ph *PanelHandler) Draw() *PanelHandler {
	ph.update()
	if ph.selectedNum > -1 {
		ph.DrawBuf(ph.buf, ph.selectedNum)

	} else {
		ph.DrawBuf(ph.buf)
	}

	return ph
}

func (ph *PanelHandler) SetSelectLine(lineno int) *PanelHandler {
	ph.selectedNum = lineno
	return ph
}

func (ph *PanelHandler) SetDrawFunc(c func()) *PanelHandler {
	ph.update = c
	return ph
}

func (ph *PanelHandler) ShowHelp() *PanelHandler {
	ph.Switch(func(parentPanel *PanelHandler) bool {
		newp := ph.Copy()
		h, w := gs.GetWindowsSize()

		newp.SetMouse(false).SetSize(w/2, h/2).SetBorder(true).SetBar(false)
		l := gs.Str("")
		for k, v := range ph.KeyMap {

			l += gs.Str("\t %v : %v\n").F(GetKeyName(k), GetFunctionName(v))
		}
		for k, v := range ph.KeyMap2 {
			l += gs.Str("\t %v : %v\n").F(string(k), GetFunctionName(v))
		}
		newp.Write(l)
		newp.Focus()
		newp.Clear()
		return true
	})
	return ph
}

func GetFunctionName(function interface{}) string {
	// Use the reflect package to get the function's name
	return runtime.FuncForPC(reflect.ValueOf(function).Pointer()).Name()

}

func GetKeyName(k termbox.Key) string {
	if c, ok := keymap[uint16(k)]; ok {
		return c
	}
	return ""
}
